package com.dbflow5.transaction;

import com.dbflow5.TriFunction;
import com.dbflow5.adapter.InternalAdapter;
import com.dbflow5.config.FlowManager;
import com.dbflow5.database.DatabaseWrapper;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

/**
 * Description: Simple interface for acting on a model in a Transaction or list of [Model]
 */
//private typealias ProcessModelList<TModel> = (List<TModel>, InternalAdapter<TModel>, DatabaseWrapper) -> Long

/**
 * Description: Similiar to [ProcessModelTransaction] in that it allows you to store a [List] of
 * [Model], except that it performs it as efficiently as possible. Also due to way the class operates,
 * only one kind of [TModel] is allowed.
 */
public class FastStoreModelTransaction<TModel> implements ITransaction<Long> {

    public List<TModel> models;
    public TriFunction<List<TModel>, InternalAdapter<TModel>, DatabaseWrapper, Long> processModelList;
    public InternalAdapter<TModel> internalAdapter;

    public FastStoreModelTransaction(Builder<TModel> builder){
        models = builder.models;
        processModelList = builder.processModelList;
        internalAdapter = builder.internalAdapter;
    }

    @Override
    public Long execute(DatabaseWrapper databaseWrapper) {
        if (models != null) {
            return processModelList.apply(models, internalAdapter, databaseWrapper);
        }
        return 0L;
    }

    /**
     * Makes it easy to build a [ProcessModelTransaction].
     *
     * @param <TModel>
    </TModel> */
    static class Builder<TModel> {

        InternalAdapter<TModel> internalAdapter;
        TriFunction<List<TModel>, InternalAdapter<TModel>, DatabaseWrapper, Long> processModelList;
        List<TModel> models = new ArrayList<>();

        Builder(InternalAdapter<TModel> internalAdapter, TriFunction<List<TModel>, InternalAdapter<TModel>, DatabaseWrapper, Long> processModelList){
            this.internalAdapter = internalAdapter;
            this.processModelList = processModelList;
        }

        public Builder<TModel> add(TModel model) {
            models.add(model);
            return this;
        }

        /**
         * Adds all specified models to the [ArrayList].
         * @param models models
         * @return Builder
         */
        @SafeVarargs
        public final Builder<TModel> addAll(TModel... models) {
            this.models.addAll(Arrays.asList(models));
            return this;
        }

        /**
         * Adds a [Collection] of [Model] to the existing [ArrayList].
         * @param models models
         * @return Builder
         */
        public Builder<TModel> addAll(Collection<TModel> models) {
            if (models != null) {
                this.models.addAll(models);
            }
            return this;
        }

        /**
         * A new [ProcessModelTransaction]
         * @return A new [ProcessModelTransaction]. Subsequent calls to this method produce
         * new instances.
         */
        FastStoreModelTransaction<TModel> build() {
            return new FastStoreModelTransaction<>(this);
        }
    }

    public static <TModel> Builder<TModel> saveBuilder(InternalAdapter<TModel> internalAdapter) {
        return new Builder<>(internalAdapter, (tModels, adapter, wrapper) -> adapter.saveAll(tModels, wrapper));
    }

    public static <TModel> Builder<TModel> insertBuilder(InternalAdapter<TModel> internalAdapter) {
        return new Builder<>(internalAdapter, (tModels, adapter, wrapper) -> adapter.insertAll(tModels, wrapper));
    }

    public static <TModel> Builder<TModel> updateBuilder(InternalAdapter<TModel> internalAdapter) {
        return new Builder<>(internalAdapter, (tModels, adapter, wrapper) -> adapter.updateAll(tModels, wrapper));
    }

    public static <TModel> Builder<TModel> deleteBuilder(InternalAdapter<TModel> internalAdapter) {
        return new Builder<>(internalAdapter, (tModels, adapter, wrapper) -> adapter.deleteAll(tModels, wrapper));
    }

    public static <T> FastStoreModelTransaction.Builder<T> fastSave(Class<T> clazz, Collection<T> collection) {
        return FastStoreModelTransaction.saveBuilder(FlowManager.modelAdapter(clazz)).addAll(collection);
    }

    public static <T> FastStoreModelTransaction.Builder<T> fastInsert(Class<T> clazz, Collection<T> collection) {
        return FastStoreModelTransaction.insertBuilder(FlowManager.modelAdapter(clazz)).addAll(collection);
    }

    public static <T> FastStoreModelTransaction.Builder<T> fastUpdate(Class<T> clazz, Collection<T> collection) {
        return FastStoreModelTransaction.updateBuilder(FlowManager.modelAdapter(clazz)).addAll(collection);
    }

    public static <T> FastStoreModelTransaction.Builder<T> fastDelete(Class<T> clazz, Collection<T> collection) {
        return FastStoreModelTransaction.deleteBuilder(FlowManager.modelAdapter(clazz)).addAll(collection);
    }
}

